#include <cad-slicer/object-transform.h>

#include <iostream>

#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/mesh.h>
#include <assimp/matrix4x4.h>

#include <prenstar/assert.h>


namespace cadslicer
{


void apply_transform(Assimp::Importer& object, float const* transform)
{
  static_assert(
        sizeof(aiMatrix4x4) == 16*sizeof(transform),
        "Invalid postulate on aiMatrix4x4. Fix this.");

  aiScene const* scene = object.GetScene();
  aiMatrix4x4 const* mat = reinterpret_cast<aiMatrix4x4 const*>(transform);

  prenstar_assert_not_nullptr(scene);

  aiMesh** current_mesh = scene->mMeshes;
  aiMesh const* const* const mesh_end = current_mesh + scene->mNumMeshes;

  // Find slice frontier for each face of each mesh.
  for (; current_mesh != mesh_end; ++current_mesh)
  {
    aiFace const* const face_end =
        (*current_mesh)->mFaces + (*current_mesh)->mNumFaces;

    for (aiFace* current_face = (*current_mesh)->mFaces;
         current_face != face_end; ++current_face)
    {
      unsigned const* const vertex_end =
          current_face->mIndices + current_face->mNumIndices;

      for (unsigned const* vertex = current_face->mIndices;
           vertex != vertex_end; ++vertex)
      {
        aiVector3D& current_vertex =
            (*current_mesh)->mVertices[*vertex];

        current_vertex *= (*mat);
      }
    }
  }
}


void apply_transform(Assimp::Importer& object, const std::array<float, 16>& transform)
{ apply_transform(object, transform.data()); }


void repair_vertice(Assimp::Importer& object, float merging_coeff)
{
  aiScene const* scene = object.GetScene();

  prenstar_assert_not_nullptr(scene);

  aiMesh** current_mesh = scene->mMeshes;
  aiMesh const* const* const mesh_end = current_mesh + scene->mNumMeshes;

  // Merge identical points.
  for (; current_mesh != mesh_end; ++current_mesh)
  {
    std::vector<unsigned> equal_vertice(
          (*current_mesh)->mNumVertices,
          std::numeric_limits<unsigned>::max());

    unsigned vertex_end = (*current_mesh)->mNumVertices;

    for (unsigned parent_vertex = 0; parent_vertex != vertex_end; ++parent_vertex)
    {
      aiVector3D const& parent = (*current_mesh)->mVertices[parent_vertex];
      for (unsigned child_vertex = parent_vertex; child_vertex != vertex_end; ++child_vertex)
      {
        aiVector3D const& child = (*current_mesh)->mVertices[child_vertex];
        if (equal_vertice[child_vertex] == std::numeric_limits<unsigned>::max() &&
            (parent - child).Length() < merging_coeff)
          equal_vertice[child_vertex] = parent_vertex;
      }
    }

    // Update faces.
    aiFace const* const face_end =
        (*current_mesh)->mFaces + (*current_mesh)->mNumFaces;

    for (aiFace* current_face = (*current_mesh)->mFaces;
         current_face != face_end; ++current_face)
    {
      unsigned const* const vertex_end =
          current_face->mIndices + current_face->mNumIndices;

      for (unsigned* vertex = current_face->mIndices;
           vertex != vertex_end; ++vertex)
      {
        if (equal_vertice[*vertex] != std::numeric_limits<unsigned>::max())
          *vertex = equal_vertice[*vertex];
      }
    }
  }

  // Remove
}


} // cadslicer
